#include "ComponentSwimAwayBehavior.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentCreatureSounds.h"
#include "Components/ComponentCreatureSounds.h"
#include "ComponentPathfinding.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemBodies.h"

#include "GameRandom.hpp"
#include "Engine/MathUtils.hpp"
#include "Log.h"
#include "GameRegistry.hpp"
#include "Managers/BlocksManager.h"
#include "Blocks/WaterBlock.h"
#include "GameSingleton.h"
#include "GameThreadPool.hpp"
#include "Segment2.h"

#include <iostream>
#include <cfloat>

namespace PocketSurvival
{

    // ************** 【状态机】 **************

    ComponentSwimAwayBehavior::StateMachine::StateMachine(ComponentSwimAwayBehavior *swimAway) : m_swimAway(swimAway), EasyMachine(MachineState::Inactive)
    {
        enter();
    }

    void ComponentSwimAwayBehavior::StateMachine::enter()
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            m_swimAway->m_importanceLevel = 0.0f;
            m_swimAway->attacker = entt::null;
            break;
        case MachineState::SwimmingAway:
            m_swimAway->componentPathfinding->setDestination(m_swimAway->findSafePlace(), 1.0f, 1.0f, 0, false, true, false, nullptr);
            break;
        }
    }

    void ComponentSwimAwayBehavior::StateMachine::update(float dt)
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            MACHINE_DELEGATE()
            {
                if (m_swimAway->attacker != entt::null)
                {
                    m_swimAway->m_timeToForgetAttacker -= dt;
                    if (m_swimAway->m_timeToForgetAttacker <= 0.0f)
                    {
                        m_swimAway->attacker = entt::null;
                    }
                }
                if (m_swimAway->componentCreature->componentHealth->healthChange < 0.0f)
                {
                    m_swimAway->m_importanceLevel = (m_swimAway->componentCreature->componentHealth->health < 0.33f) ? 300.0f : 100.0f;
                }
                else if (m_swimAway->attacker != entt::null)
                {
                    if (GameSingleton::gameRegistry->valid(m_swimAway->attacker))
                    {
                        ComponentBody *attackerBody = GameSingleton::gameRegistry->get<ComponentBody*>(m_swimAway->attacker);

                        Vector3 attackerPosition;
                        Vector3 bodyPosition;
                        attackerBody->getPosition(attackerPosition);
                        m_swimAway->componentCreature->componentBody->getPosition(bodyPosition);

                        if (Vector3::Distance(attackerPosition, bodyPosition) < 25.0f)
                        {
                            m_swimAway->m_importanceLevel = 100.0f;
                        }
                    }
                    else
                    {
                        m_swimAway->attacker = entt::null;
                    }
                }
            }
            break;
        case MachineState::SwimmingAway:
            MACHINE_DELEGATE()
            {
                if (m_swimAway->isActive() == false || m_swimAway->componentPathfinding->destination == FastVector3::Zero || m_swimAway->componentPathfinding->isStuck)
                {
                    changeTo(MachineState::Inactive);
                }
            }
            break;
        }
    }

    void ComponentSwimAwayBehavior::StateMachine::leave()
    {
        switch (m_currentState)
        {
        case MachineState::Inactive:
            break;
        case MachineState::SwimmingAway:
            m_swimAway->m_importanceLevel = 0.0f;
            break;
        }
    }

    // **************************************



    ComponentSwimAwayBehavior::~ComponentSwimAwayBehavior()
    {
        delete m_stateMachine;
    }

    float ComponentSwimAwayBehavior::getImportanceLevel()
    {
        return m_importanceLevel;
    }

    void ComponentSwimAwayBehavior::update(float dt)
    {
        m_stateMachine->update(dt);
    }


    void ComponentSwimAwayBehavior::swimAwayFrom(entt::entity theAttacker)
    {
        attacker = theAttacker;
        m_timeToForgetAttacker = GameSingleton::gameRandom->getFloat(10.0f, 20.0f);
    }

    Vector3 ComponentSwimAwayBehavior::findSafePlace()
    {
        BoundingBox boundingBox = componentBody->getBoundingBox();
        Vector3 vector = 0.5f * (boundingBox.Min + boundingBox.Max);

        std::shared_ptr<Vector3> herdPosition = nullptr;
        float num = -FLT_MAX;
        Vector3 result = vector;
        for (int32_t index = 0; index < 40; index++)
        {
            Vector2 vector2 = GameSingleton::gameRandom->getVector2(1.0f, 1.0f);
            float y = 0.4f * GameSingleton::gameRandom->getFloat(-1.0f, 1.0f);
            Vector3 v = Vector3::Normalize(Vector3(vector2.posX, y, vector2.posY));
            Vector3 vector3 = vector + GameSingleton::gameRandom->getFloat(10.0f, 20.0f) * v;
            std::shared_ptr<TerrainRaycastResult> terrainRaycastResult = subsystemTerrain->raycast(vector, vector3, false, false, [](int32_t value, float d) -> bool
            {
            	int32_t num3 = Terrain::ExtractContents(value);
            	return !(GameSingleton::blocksManager->blocks[num3]->isBlock<WaterBlock *>());
            });
            Vector3 vector4 = terrainRaycastResult != nullptr ? (vector + v * terrainRaycastResult->distance) : vector3;
            float num2 = scoreSafePlace(vector, vector4, herdPosition);
            if (num2 > num)
            {
            	num = num2;
            	result = vector4;
            }
        }
        return result;
    }

    float ComponentSwimAwayBehavior::scoreSafePlace(const Vector3 &currentPosition, const Vector3 &safePosition, std::shared_ptr<Vector3> herdPosition)
    {
        Vector2 vector(currentPosition.posX, currentPosition.posZ);
        Vector2 vector2(safePosition.posX, safePosition.posZ);
        std::shared_ptr<Vector2> vector3 = nullptr;
        if (herdPosition != nullptr)
        {
            vector3 = std::make_shared<Vector2>(herdPosition->posX, herdPosition->posZ);
        }
        Segment2 s(vector, vector2);
        float num = 0.0f;
        if (vector3 != nullptr)
        {
            num = Segment2::Distance(s, *vector3);
        }
        if (attacker != entt::null)
        {
            if (GameSingleton::gameRegistry->valid(attacker))
            {
                ComponentBody *attackerBody = GameSingleton::gameRegistry->get<ComponentBody*>(attacker);

                Vector3 position;
                attackerBody->getPosition(position);

                Vector2 vector4(position.posX, position.posZ);
                float num2 = Vector2::Distance(vector4, vector2);
                float num3 = Segment2::Distance(s, vector4);
                return num2 + 1.5f * num3 - num;
            }
            attacker = entt::null;
        }
        return 1.5f * Vector2::Distance(vector, vector2) - num;
    }

    static const std::string componentName = "SwimAwayBehavior";
    const std::string &ComponentSwimAwayBehavior::getName() const
    {
        return componentName;
    }
    const std::string &ComponentSwimAwayBehavior::GetName()
    {
        return componentName;
    }
    void ComponentSwimAwayBehavior::load(const nlohmann::json &jsonData)
    {
        componentCreature = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);
        componentPathfinding = GameSingleton::gameRegistry->get<ComponentPathfinding *>(entity);
        componentBody = GameSingleton::gameRegistry->get<ComponentBody *>(entity);
        componentHealth = GameSingleton::gameRegistry->get<ComponentHealth *>(entity);

        subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        componentHealth->addAttackedEvent(std::bind(&ComponentSwimAwayBehavior::swimAwayFrom, this, std::placeholders::_1));

        m_stateMachine = new StateMachine(this);
    }

}
